
#include "RtBase.h"
#include "RtChannelHttpClient.h"
#include "RtHttpUrl.h"
#include "RtInetAddr.h"
#include "RtMessageBlock.h"
#include "RtHttpProxyManager.h"

#ifdef RT_WIN32
  #include "RtHttpNTLMAuth.h"
#endif // RT_WIN32

CRtChannelHttpClient::
CRtChannelHttpClient(CRtHttpUrl *aURL, CRtHttpProxyInfo *aPi)
	: CRtChannelHttpBase(this)
	, m_pProxyInfo(aPi)
	, m_pUrl(aURL)
	, m_ParserResponse(m_ResponseHeader, this)
	, m_dwAuthTimes(3)
	, m_bIsAuthing(0)
	, m_FlagConnector(0)
	, m_bNeedFirstNTLMAuth(TRUE)
	, m_bWWWAuth(FALSE)
//	, m_bFullResponse(FALSE)
{
	RT_ASSERTE(m_pUrl);
	RT_INFO_TRACE_THIS("CRtChannelHttpClient::CRtChannelHttpClient,"
		" aURL=" << aURL->GetAsciiSpec() << 
		" aPi=" << aPi);

	m_RequestHeader.SetVersion(HttpVersion_1_1);
	SetRequestMethod_i(CRtHttpAtomList::Get);

	m_RequestHeader.SetHeader(CRtHttpAtomList::Host, m_pUrl->GetNameAndPort());
	m_RequestHeader.SetHeader(CRtHttpAtomList::Accept, "*/*");
	m_RequestHeader.SetHeader(CRtHttpAtomList::User_Agent, "httpclient");

	if (m_pProxyInfo && !m_pProxyInfo->GetUserName().empty())
		m_bNeedFirstNTLMAuth = FALSE;
}

CRtChannelHttpClient::
CRtChannelHttpClient(CRtHttpUrl *aURL, CRtChannelManager::CFlag aFlag)
	: CRtChannelHttpBase(this)
	, m_pUrl(aURL)
	, m_ParserResponse(m_ResponseHeader, this)
	, m_dwAuthTimes(3)
	, m_bIsAuthing(0)
	, m_FlagConnector(aFlag)
	, m_bNeedFirstNTLMAuth(TRUE)
	, m_bWWWAuth(FALSE)
//	, m_bFullResponse(FALSE)
{
	RT_ASSERTE(m_pUrl);
	RT_INFO_TRACE_THIS("CRtChannelHttpClient::CRtChannelHttpClient,"
		" aURL=" << aURL->GetAsciiSpec() << 
		" aFlag=" << aFlag);

	m_RequestHeader.SetVersion(HttpVersion_1_1);
	SetRequestMethod_i(CRtHttpAtomList::Get);

	m_RequestHeader.SetHeader(CRtHttpAtomList::Host, m_pUrl->GetNameAndPort());
	m_RequestHeader.SetHeader(CRtHttpAtomList::Accept, "*/*");
	m_RequestHeader.SetHeader(CRtHttpAtomList::User_Agent, "httpclient");
}

CRtChannelHttpClient::~CRtChannelHttpClient()
{
	Disconnect(RT_OK);
}

DWORD CRtChannelHttpClient::AddReference()
{
	return CRtReferenceControlSingleThreadTimerDelete::AddReference();
}

DWORD CRtChannelHttpClient::ReleaseReference()
{
	return CRtReferenceControlSingleThreadTimerDelete::ReleaseReference();
}

RtResult CRtChannelHttpClient::AsyncOpen(IRtChannelSink *aSink)
{
	// we don't allow AsyncOpen() when dialog for auth.
	RT_ASSERTE(!m_bIsAuthing);

	RT_ASSERTE_RETURN(aSink, RT_ERROR_INVALID_ARG);
	if (m_pSink != aSink) {
		// authorization will invoke this function again.
		RT_ASSERTE(!m_pSink);
		m_pSink = aSink;
		m_pChannelSink = aSink;

		if (m_pProxyInfo)
			AddAuthInfo(m_pProxyInfo->GetAuthScheme());
	}

	CRtString strHost;
	WORD port;
	if (m_pProxyInfo) {
		m_RequestHeader.SetHeader(CRtHttpAtomList::Proxy_Connection, "keep-alive");
		m_RequestHeader.SetHeader(CRtHttpAtomList::Connection, CRtString());

		strHost = m_pProxyInfo->GetHostName();
		port = m_pProxyInfo->GetPort();
	}
	else {
		m_RequestHeader.SetHeader(CRtHttpAtomList::Connection, "keep-alive");
		m_RequestHeader.SetHeader(CRtHttpAtomList::Proxy_Connection, CRtString());

		strHost = m_pUrl->GetHostName();
		port = m_pUrl->GetPort();
	}

	m_bIsFirstSend = TRUE;
	
	CRtInetAddr addrSvr(strHost.c_str(), port);
	RtResult rv = Connect_i(addrSvr);
	return rv;
}

RtResult CRtChannelHttpClient::SendData(
		CRtMessageBlock& inData,
		BOOL inSendImmediately,
		BOOL inDestroyData,
		DataPriority inPriority, 
		BOOL inNeedAck)
{
	RtResult rv = RT_OK;
	RT_ASSERTE_RETURN(m_pTransport, RT_ERROR_NOT_INITIALIZED);
	if (m_bIsFirstSend) {

		/// add HTTP request header.

		if (!m_customContentLength) {
			rv = SetContentLength_i(m_RequestHeader, &inData);
			//m_bSetPath = FALSE;
			if (RT_FAILED(rv))
				return rv;
		}

		CRtString strRequest = m_RequestHeader.Flatten();
		strRequest += CRtHttpHeaderArray::s_szHttpHeaderNewLine;

		CRtMessageBlock mbHead(
			strRequest.length(),
			const_cast<LPSTR>(strRequest.c_str()),
			CRtMessageBlock::DONT_DELETE,
			strRequest.length());
// 		if (inData.GetChainedLength() > 0)
			mbHead.Append(&inData);

		if (inData.GetChainedLength() > 0) {
			RT_ASSERTE(m_RequestHeader.GetMethod() == CRtHttpAtomList::Post);
			if (m_strFirstSend.empty())
				m_strFirstSend = inData.FlattenChained();
		}
		
		rv = SendData_i(mbHead);
		if (RT_FAILED(rv)) {
			RT_ERROR_TRACE_THIS("CRtChannelHttpClient::SendPrivateData, it's impossible that sending first packet failed!");
			return rv;
		}

		if(m_bStream)
			m_bIsFirstSend = FALSE;
	}
	else if (inData.GetChainedLength() > 0) {
		rv = SendData_i(inData);
	}
	else {
		RT_ASSERTE(FALSE);
		rv = RT_ERROR_INVALID_ARG;
	}
	
	if (RT_SUCCEEDED(rv) && inDestroyData)
		inData.DestroyChained();

	return rv;
}

RtResult CRtChannelHttpClient::GetUrl(CRtHttpUrl *&aUrl)
{
	aUrl = m_pUrl.Get();
	if (aUrl) {
		aUrl->AddReference();
		return RT_OK;
	}
	else {
		return RT_ERROR_NOT_INITIALIZED;
	}
}

RtResult CRtChannelHttpClient::Disconnect(RtResult aReason)
{
	m_strFirstSend.resize(0);
	return Close_t(aReason);
}

RtResult CRtChannelHttpClient::OpenWithSink(IRtTransportSink *aSink)
{
	RT_ASSERTE(!"CRtChannelHttpClient::OpenWithSink");
	return RT_ERROR_UNEXPECTED;
}

IRtTransportSink* CRtChannelHttpClient::GetSink()
{
	return m_pSink;
}

RtResult CRtChannelHttpClient::SetOption(DWORD aCommand, LPVOID aArg)
{
	switch(aCommand) {
	case RT_OPT_CHANNEL_HTTP_PARSER_SKIP_CONTENT_LENGTH:
		m_ParserResponse.m_bSkipContentLength = *static_cast<BOOL*>(aArg);
		return RT_OK;

	default:
		RtResult rv = CRtChannelHttpBase::SetOption_i(aCommand, aArg);
/*		if(RT_OK == rv && aCommand == RT_OPT_CHANNEL_HTTP_DELEVER_PARTIAL_DATA)
		{
			 m_ParserResponse.SetDeleverFlag(*static_cast<BOOL*>(aArg));
		}*/
		return rv;
	}
}

RtResult CRtChannelHttpClient::GetOption(DWORD aCommand, LPVOID aArg)
{
	switch(aCommand) {
	case RT_OPT_CHANNEL_HTTP_PARSER_SKIP_CONTENT_LENGTH:
		if (!aArg)
			return RT_ERROR_INVALID_ARG;
		*static_cast<BOOL*>(aArg) = m_ParserResponse.m_bSkipContentLength;
		return RT_OK;
	
	case RT_OPT_TRANSPORT_TRAN_TYPE: {
		if (!aArg)
			return RT_ERROR_INVALID_ARG;
		CRtConnectionManager::CType *pType = static_cast<CRtConnectionManager::CType*>(aArg);
		*pType = CRtConnectionManager::CTYPE_HTTP;
		if (m_pUrl && m_pUrl->GetScheme() == CRtHttpUrl::s_pszSchemeHttps)
			*pType = CRtConnectionManager::CTYPE_HTTPS;
		return RT_OK;
									 }

	default:
		return CRtChannelHttpBase::GetOption_i(aCommand, aArg);
	}
}

RtResult CRtChannelHttpClient::
SetPath( const CRtString& aPath )
{
	RT_ASSERTE_RETURN(!aPath.empty(), RT_ERROR_NOT_FOUND);

	m_RequestHeader.SetRequestURI( aPath );
	return RT_OK;
}

RtResult CRtChannelHttpClient::
SetOrAddRequestHeader(const CRtString &aHeader, const CRtString &aValue)
{
	CRtHttpAtom atHead = CRtHttpAtomList::Instance()->ResolveAtom(aHeader);
	RT_ASSERTE_RETURN(atHead.get(), RT_ERROR_NOT_FOUND);

	BOOL bRet = m_RequestHeader.SetHeader(atHead, aValue);
	return bRet ? RT_OK : RT_ERROR_FAILURE;
}

RtResult CRtChannelHttpClient::SetRequestMethod(const CRtString &aMethod)
{
	CRtHttpAtom atMethod = CRtHttpAtomList::Instance()->ResolveAtom(aMethod, FALSE);
	if (!atMethod.get())
		return RT_ERROR_NOT_FOUND;

	return SetRequestMethod_i(atMethod);
}

RtResult CRtChannelHttpClient::SetRequestMethod_i(const CRtHttpAtom &aMethod)
{
	CRtString strGet;
	if (aMethod == CRtHttpAtomList::Connect) {
		strGet = m_pUrl->GetNameAndPort(TRUE);
//		m_RequestHeader.SetHeader(CRtHttpAtomList::Content_Length, "0");
	} else if (m_pProxyInfo) {
		strGet = m_pUrl->GetAsciiSpec();
	} else {
		strGet = m_pUrl->GetPath();
	}

	CRtString::size_type nFind = strGet.find('#', 0);
	if (nFind != CRtString::npos)
		strGet.resize(nFind);
	m_RequestHeader.SetRequestURI(strGet);

	BOOL bRet = m_RequestHeader.SetMethod(aMethod);
	return bRet ? RT_OK : RT_ERROR_FAILURE;
}

RtResult CRtChannelHttpClient::GetRequestMethod(CRtString &aMethod)
{
	aMethod = m_RequestHeader.GetMethod().get();
	return !aMethod.empty() ? RT_OK : RT_ERROR_FAILURE;
}

RtResult CRtChannelHttpClient::GetResponseStatus(LONG &aStatus)
{
	aStatus = m_ResponseHeader.GetStatus();
	return aStatus == -1 ? RT_ERROR_FAILURE : RT_OK;
}

RtResult CRtChannelHttpClient::
GetResponseHeader(const CRtString &aHeader, CRtString &aValue)
{
	CRtHttpAtom atHead = CRtHttpAtomList::Instance()->ResolveAtom(aHeader);
	RT_ASSERTE_RETURN(atHead.get(), RT_ERROR_NOT_FOUND);

	BOOL bRet = m_ResponseHeader.GetHeader(atHead, aValue);
	return bRet ? RT_OK : RT_ERROR_FAILURE;
}

RtResult CRtChannelHttpClient::Connect_i(const CRtInetAddr &aAddrPeer)
{
	RT_ASSERTE(!m_pConnector);

	CRtConnectionManager::CType typeConnector;
	if (m_pUrl->GetScheme() == CRtHttpUrl::s_pszSchemeHttps &&
		m_RequestHeader.GetMethod() != CRtHttpAtomList::Connect) 
	{
		typeConnector = CRtConnectionManager::CTYPE_SSL_DIRECT;
		if (RT_BIT_ENABLED(m_FlagConnector, CRtChannelManager::CFLAG_WITH_BROWER_PROXY))
			RT_SET_BITS(typeConnector, CRtConnectionManager::CTYPE_SSL_WITH_BROWER_PROXY);
	}
	else {
		typeConnector = CRtConnectionManager::CTYPE_TCP;
		// don't check CFLAG_WITH_BROWER_PROXY because we use class <CRtChannelHttpClientWithBrowerProxy>.
//		if (RT_BIT_ENABLED(m_FlagConnector, CRtChannelManager::CFLAG_WITH_BROWER_PROXY))
//			RT_SET_BITS(typeConnector, CRtConnectionManager::CTYPE_TCP_WITH_BROWER_PROXY);
	}
	if (RT_BIT_ENABLED(m_FlagConnector, CRtChannelManager::CFLAG_SEND_NO_PARTIAL_DATA))
		RT_SET_BITS(typeConnector, CRtConnectionManager::CTYPE_SEND_NO_PARTIAL_DATA);

	RtResult rv = CRtConnectionManager::Instance()->CreateConnectionClient(
		typeConnector,
		m_pConnector.ParaOut());
	if (RT_FAILED(rv))
		return rv;

	CRtTimeValue tvOut(60, 0);
	m_pConnector->AsycConnect(this, aAddrPeer, &tvOut);
	return RT_OK;
}

void CRtChannelHttpClient::
OnConnectIndication(RtResult aReason, IRtTransport *aTrpt, 
					IRtAcceptorConnectorId *aRequestId)
{
	RT_INFO_TRACE_THIS("CRtChannelHttpClient::OnConnectIndication, rv="<<aReason<<" trpt="<<aTrpt);
	RT_ASSERTE(m_pConnector.Get() == aRequestId);
	RT_ASSERTE(!m_pTransport);

	RtResult rv = aReason;
	if (RT_SUCCEEDED(rv)) {
		rv = aTrpt->OpenWithSink(this);
		if (RT_SUCCEEDED(rv)) {
			m_pTransport = aTrpt;
		}
	}

	RT_ASSERTE(m_pChannelSink);
	if (m_pChannelSink)
		m_pChannelSink->OnConnect(rv, this);
}

RtResult CRtChannelHttpClient::Close_t(RtResult aReason)
{
	if (m_pConnector) {
		m_pConnector->CancelConnect();
		m_pConnector = NULL;
	}
//	m_pUrl = NULL;
//	m_pProxyInfo = NULL;

	IRtHttpAuthInfoGetter *pGetter = 
		CRtHttpProxyManager::Instance()->GetHttpAuthInfoGetter();
	if (pGetter)
		pGetter->InterruptGetAuthInfoBlocked(this);

	return CRtChannelHttpBase::Close_t(aReason);
}

RtResult CRtChannelHttpClient::
ParserHttpProtocol_t(CRtMessageBlock &aData)
{
	if(m_bFullResponse)
	{
		m_bFullResponse = FALSE;
		m_ParserResponse.Clear();
	}
	RtResult rv = RT_OK;
	if (m_ParserResponse.IsFinished())
	{
		return rv;
	}

	rv = m_ParserResponse.Analyse(aData);
	if (RT_SUCCEEDED(rv)) {
		switch(m_ResponseHeader.GetStatus()) {
		case 200: // It's OK
			break;
			
		case 401:
			RT_INFO_TRACE_THIS("CRtChannelHttpClient::ParserHttpProtocol_t, status="<<m_ResponseHeader.GetStatus());
		case 407:
			this->AddReference();
			RT_ASSERTE(!m_bIsAuthing);
			m_bIsAuthing = TRUE;
			rv = ProcessAuthorization();
			m_bIsAuthing = FALSE;
			this->ReleaseReference();

			if (!m_pSink) {
				RT_WARNING_TRACE_THIS("CRtChannelHttpClient::ParserHttpProtocol_t,"
					" channel is closed when dialog.");
				RT_ASSERTE(this->GetReference() == 0);

				// we return RT_ERROR_PARTIAL_DATA in order to 
				// let CRtChannelHttpBase::OnReceive return at once.
				return RT_ERROR_PARTIAL_DATA;
			}

			if (rv == RT_ERROR_WOULD_BLOCK) {
				m_bIsAuthing = TRUE;
				return RT_ERROR_PARTIAL_DATA;
			}

			if (RT_FAILED(rv)) {
				if (m_pProxyInfo) {
					m_pProxyInfo->SetUserName(CRtString());
					m_pProxyInfo->SetPasswd(CRtString());
				}
				return rv;
			}

			rv = DoAuthorizationOpen_i();
			if (RT_SUCCEEDED(rv))
				rv = RT_ERROR_PARTIAL_DATA;
			return rv;

		case 501: // not implemented
		case 502: // bad gateway
		case 503: // proxy connected server failed.
		case 505: // HTTP version not supported
			m_dwAuthTimes = 0;
			RT_WARNING_TRACE_THIS("CRtChannelHttpClient::ParserHttpProtocol_t"
				" status=" << m_ResponseHeader.GetStatus() << 
				" text=" << m_ResponseHeader.GetStatusText());
			rv = RT_ERROR_NETWORK_PROXY_SERVER_UNAVAILABLE;
			break;
		//case 401:	
		default:
			RT_WARNING_TRACE_THIS("CRtChannelHttpClient::ParserHttpProtocol_t"
				" status=" << m_ResponseHeader.GetStatus() << 
				" text=" << m_ResponseHeader.GetStatusText());
			rv = RT_ERROR_FAILURE;
			break;
		}
		
		CRtMessageBlock *pMb = NULL;
		rv = m_ParserResponse.GetContent(pMb);
		if (RT_SUCCEEDED(rv)) {
			CRtMessageBlock mbTmp(0);
			if (!pMb)
				pMb = &mbTmp;
			
			RT_ASSERTE(m_pSink);
			if (m_pSink/* && pMb->GetChainedLength() > 0*/) //only have http header
				m_pSink->OnReceive(*pMb, this);
			
			if (pMb != &mbTmp)
				pMb->DestroyChained();
		}

	}
	return rv;
}

RtResult CRtChannelHttpClient::DoAuthorizationOpen_i()
{
	RT_INFO_TRACE_THIS("CRtChannelHttpClient::DoAuthorizationOpen_i()");
	RtResult rv;
	// we don't buffer request data when SendData().
//	RT_ASSERTE(m_RequestHeader.GetContentLength() == 0);
	
	m_ParserResponse.Clear();
	m_bIsFirstSend = TRUE;
	if (IsTransportAlive()) {
		CRtMessageBlock mbZero(
			m_strFirstSend.size(), 
			m_strFirstSend.c_str(), 
			CRtMessageBlock::DONT_DELETE, 
			m_strFirstSend.size());
		rv = SendData(mbZero);
		RT_INFO_TRACE_THIS("CRtChannelHttpClient::DoAuthorizationOpen_i() Send Data rv = " << rv);
	}
	else {
		RT_WARNING_TRACE_THIS("CRtChannelHttpClient::DoAuthorizationOpen_i, trpt not alive! trpt="<<m_pTransport<<" connector="<<m_pConnector);
		if (m_pConnector) {
			m_pConnector->CancelConnect();
			m_pConnector = NULL;
		}
		if (m_pTransport) {
			m_pTransport->Disconnect(RT_OK);
			m_pTransport = NULL;
		}
#if 1
		CRtEventHttpClientAsyncOpen* event = new CRtEventHttpClientAsyncOpen(this);
		rv = CRtThreadManager::Instance()->GetThreadEventQueue(CRtThreadManager::TT_CURRENT)->PostEvent(event);
#else
		rv = CRtChannelHttpClient::AsyncOpen(m_pChannelSink);
#endif
	}
	return rv;
}

RtResult CRtChannelHttpClient::ProcessAuthorization()
{
	RtResult rv = RT_ERROR_FAILURE;
	if (!m_pProxyInfo) {
		RT_ERROR_TRACE_THIS("CRtChannelHttpClient::ProcessAuthorization,"
			" there is no proxy, should be www authorization, url="<<m_pUrl->GetAsciiSpec());
		m_pProxyInfo = new CRtHttpProxyInfo();
		m_pProxyInfo->Init(m_pUrl->GetNameAndPort(), CRtHttpProxyInfo::HTTP_PROXY);
		m_bWWWAuth = TRUE;
		//return rv;
	}
	
	if (m_pHttpAuthenticator && m_pHttpAuthenticator->HaveAuthenticateComplete())  {
		m_pProxyInfo->SetUserName(CRtString());
		m_pProxyInfo->SetPasswd(CRtString());

		BOOL bFirst = m_bNeedFirstNTLMAuth && 
			m_pHttpAuthenticator->AllowFirstTryWithoutUserName();
		m_pHttpAuthenticator = NULL;
		m_RequestHeader.SetHeader(m_bWWWAuth?CRtHttpAtomList::Authorization : CRtHttpAtomList::Proxy_Authorization, CRtString());

		// we allow first NTLM auth without user's inputting his name and password
//		if (bFirst)
//			m_pProxyInfo->DescreaseFirstTryedWithoutUserName();
		m_bNeedFirstNTLMAuth = FALSE;

		if (!bFirst && m_dwAuthTimes <= 0) {
			RT_WARNING_TRACE_THIS("CRtChannelHttpClient::ProcessAuthorization,"
				" exceed auth times.");
			return rv;
		}
		m_dwAuthTimes--;
	}

	CRtString strChallenge, strScheme, strChallengeAll;
	if(!m_bWWWAuth)
		m_ResponseHeader.GetHeader(CRtHttpAtomList::Proxy_Authenticate, strChallengeAll);
	else
		m_ResponseHeader.GetHeader(CRtHttpAtomList::WWW_Authenticate, strChallengeAll);

	if (!m_pHttpAuthenticator) {
		CRtString::size_type nFirst = 0, nFind = 0;
		do {
			nFind = strChallengeAll.find('\n', nFirst);
			if (nFind != CRtString::npos)
				strChallenge = strChallengeAll.substr(nFirst, nFind - nFirst);
			else
				strChallenge = strChallengeAll.substr(nFirst);
			strScheme = IRtHttpAuthenticator::GetSchemeFromChallenge(strChallenge);
			if (!m_pHttpAuthenticator) {
				m_pHttpAuthenticator = IRtHttpAuthenticator::GetAuthenticatorFromScheme(strScheme);
				if (m_pHttpAuthenticator) 
					break;
			}
			nFirst = nFind +1;
		} while (nFind != CRtString::npos);

		if (!m_pHttpAuthenticator) {
			RT_ERROR_TRACE_THIS("CRtChannelHttpClient::ProcessAuthorization,"
				" unknown strChallengeAll=" << strChallengeAll);
			return RT_ERROR_NOT_FOUND;
		}
		if (m_pHttpAuthenticator->AreCredentialsReusable())
			m_pProxyInfo->SetAuthScheme(strScheme);
		m_strSchemeFirstBuffer = strScheme;
#ifdef RT_WIN32
		if (strScheme == CRtHttpNTLMAuth::s_pszNTLMAuth) {
			if (m_pConnector) {
				m_pConnector->CancelConnect();
				m_pConnector = NULL;
			}
			if (m_pTransport) {
				m_pTransport->Disconnect(RT_OK);
				m_pTransport = NULL;
			}
		}
#endif // RT_WIN32
	} else {
		strChallenge = strChallengeAll;
		strScheme = m_pProxyInfo->GetAuthScheme();
	}

	rv = RT_OK;
	CRtString strUserName = m_pProxyInfo->GetUserName();
	if (strUserName.empty()) {
		if (m_bNeedFirstNTLMAuth && m_pHttpAuthenticator->AllowFirstTryWithoutUserName())
		{
			RT_INFO_TRACE_THIS("CRtChannelHttpClient::ProcessAuthorization, First Try Without User Name");
		}
		else {
			RT_WARNING_TRACE_THIS("CRtChannelHttpClient::ProcessAuthorization,"
				" need input user name and password.");

			IRtHttpAuthInfoGetter::GETTER_RESULT wr;
			wr = CRtHttpProxyManager::Instance()->
				GetAuthInfoFromDialog(strChallenge, m_pProxyInfo.ParaIn(), this);
			
			if (wr == IRtHttpAuthInfoGetter::GETTER_OK) {
				rv = RT_OK;
			} 
			else if (wr == IRtHttpAuthInfoGetter::GETTER_CANCEL) {
				m_dwAuthTimes = 0;
				rv = RT_ERROR_NETWORK_PROXY_SERVER_UNAVAILABLE;
			}
			else if (wr == IRtHttpAuthInfoGetter::GETTER_WOULDBLOCK) {
				rv = RT_ERROR_WOULD_BLOCK;
			}
			else {
				rv = RT_ERROR_FAILURE;
			}
		}
	}

	if (RT_SUCCEEDED(rv))
		rv = AddAuthInfo(strChallenge);
	if (RT_FAILED(rv) && rv != RT_ERROR_WOULD_BLOCK)
		m_pHttpAuthenticator = NULL;
	RT_INFO_TRACE_THIS("CRtChannelHttpClient::ProcessAuthorization() rv = " << rv);
	return rv;
}

RtResult CRtChannelHttpClient::AddAuthInfo(const CRtString &aChallenge)
{
	RT_ASSERTE(m_pProxyInfo);

	if (!m_pHttpAuthenticator) {
		m_pHttpAuthenticator = 
			IRtHttpAuthenticator::GetAuthenticatorFromScheme(aChallenge);
		if (!m_pHttpAuthenticator) 
			return RT_ERROR_NOT_FOUND;
	} 

	CRtString strAuth = m_pHttpAuthenticator->GenerateCredentials(
		this, 
		aChallenge.c_str(), 
		m_pProxyInfo->GetUserName().c_str(), 
		m_pProxyInfo->GetPasswd().c_str());
	if (strAuth.empty())
		return RT_ERROR_FAILURE;

	m_RequestHeader.SetHeader(
		m_bWWWAuth?CRtHttpAtomList::Authorization : CRtHttpAtomList::Proxy_Authorization, 
		strAuth);
	return RT_OK;
}

void CRtChannelHttpClient::TransferTransport(IRtTransport *&pTransport)
{
	pTransport = m_pTransport.Get();
	if (pTransport)
		pTransport->AddReference();
	m_pTransport = NULL;
}

BOOL CRtChannelHttpClient::IsTransportAlive()
{
	BOOL bAlive = FALSE;
	if (m_pTransport)
		m_pTransport->GetOption(RT_OPT_TRANSPORT_SOCK_ALIVE, &bAlive);
	return bAlive;
}

void CRtChannelHttpClient::
OnDisconnect(RtResult aReason, IRtTransport *aTrptId)
{
	RT_WARNING_TRACE_THIS("CRtChannelHttpClient::OnDisconnect, rv="
		<<aReason<<" trpt="<<aTrptId<<" m_bIsAuthing="<<m_bIsAuthing);
	// ISA2004 will RST connection if try first NTLM.
	if (m_pProxyInfo && 
		m_bNeedFirstNTLMAuth &&
		m_pHttpAuthenticator && 
		m_pHttpAuthenticator->AllowFirstTryWithoutUserName())
	{
		RT_WARNING_TRACE_THIS("CRtChannelHttpClient::OnDisconnect, NTLM again!");
		RT_ASSERTE(!m_bIsAuthing);
//		m_pProxyInfo->DescreaseFirstTryedWithoutUserName();
		m_bNeedFirstNTLMAuth = FALSE;
		m_pHttpAuthenticator = NULL;
		m_RequestHeader.SetHeader(m_bWWWAuth?CRtHttpAtomList::Authorization : CRtHttpAtomList::Proxy_Authorization, CRtString());
		
		RtResult rv = DoAuthorizationOpen_i();
		if (RT_SUCCEEDED(rv))
			return;
	}

	if (!m_bIsAuthing) {
		CRtChannelHttpBase::OnDisconnect(aReason, aTrptId);
	}
	else {
		RT_WARNING_TRACE_THIS("CRtChannelHttpClient::OnDisconnect,"
			" dialog auth, don't callback OnDisconnect().");
		if (m_pConnector) {
			m_pConnector->CancelConnect();
			m_pConnector = NULL;
		}
		if (m_pTransport) {
			m_pTransport->Disconnect(RT_OK);
			m_pTransport = NULL;
		}
	}
}

void CRtChannelHttpClient::OnObserve(LPCSTR aTopic, LPVOID aData)
{
	if (!strcmp(aTopic, "AuthInfoGetter")) {
		RT_ASSERTE(m_bIsAuthing);
		m_bIsAuthing = FALSE;

		RtResult rv = RT_ERROR_FAILURE;
		IRtHttpAuthInfoGetter::CObserverBuffer bufResult = *static_cast<IRtHttpAuthInfoGetter::CObserverBuffer *>(aData);
		RT_INFO_TRACE_THIS("CRtChannelHttpClient::OnObserve,"
			" m_Result=" << bufResult.m_Result << 
			" UserName=" << bufResult.m_strUserName << 
			" Password=" << bufResult.m_strPassword);

		if (bufResult.m_Result == IRtHttpAuthInfoGetter::GETTER_OK) {
			m_pHttpAuthenticator = NULL;
			if (m_pProxyInfo) {
				m_pProxyInfo->SetUserName(bufResult.m_strUserName);
				m_pProxyInfo->SetPasswd(bufResult.m_strPassword);
				AddAuthInfo(m_strSchemeFirstBuffer);
			}
			rv = DoAuthorizationOpen_i();
		}
		if (RT_FAILED(rv)) {
			CRtChannelHttpBase::OnDisconnect(rv, m_pTransport.ParaIn());
		}
	}
}


CRtChannelHttpClientWithBrowerProxy::
CRtChannelHttpClientWithBrowerProxy(CRtHttpUrl *aURL, CRtChannelManager::CFlag aFlag)
	: CRtChannelHttpClient(aURL, aFlag)
	, m_pHttpProxyManager(NULL)
	, m_pSinkTemp(NULL)
	, m_dwConnectCount(0)
	, m_bOnConnectCalled(FALSE)
{
	RT_ASSERTE(RT_BIT_ENABLED(aFlag, CRtChannelManager::CFLAG_WITH_BROWER_PROXY));
	RT_ASSERTE(m_pUrl->GetScheme() == CRtHttpUrl::s_pszSchemeHttp);
	
	m_pHttpProxyManager = CRtHttpProxyManager::Instance();
}

CRtChannelHttpClientWithBrowerProxy::~CRtChannelHttpClientWithBrowerProxy()
{
	Close_t(RT_OK);
}

RtResult CRtChannelHttpClientWithBrowerProxy::AsyncOpen(IRtChannelSink *aSink)
{
	RT_ASSERTE_RETURN(aSink, RT_ERROR_INVALID_ARG);
	RT_ASSERTE(!m_pSinkTemp || m_pSinkTemp == aSink);
	m_pSinkTemp = aSink;

	if (m_dwConnectCount++ == 0) {
		// it's first connection, use TCP directly.
		RT_ASSERTE(!m_pProxyInfo);
		return CRtChannelHttpClient::AsyncOpen(aSink);
	}

	for ( ; ; ) {
		IRtHttpProxyInfoGetter::NextReturn nrGet;
		nrGet = m_pHttpProxyManager->GetProxyInfo(
			m_pUrl->GetHostName(),
			m_pUrl->GetPort(),
			m_pProxyInfo.ParaOut());
		if (nrGet == IRtHttpProxyInfoGetter::NEXT_SUCCESS) {
			SetRequestMethod_i(m_RequestHeader.GetMethod());
			if (!m_pProxyInfo->GetUserName().empty())
				m_bNeedFirstNTLMAuth = FALSE;
			return CRtChannelHttpClient::AsyncOpen(aSink);
		}
		else if (nrGet == IRtHttpProxyInfoGetter::NEXT_WOULDBLCOK) {
			m_pHttpProxyManager->AddObserver(this);
			return RT_OK;
		}
		else {
			RT_WARNING_TRACE_THIS("CRtChannelHttpClientWithBrowerProxy::AsyncOpen,"
				" GetProxyInfo() none."
				" addr=" << m_pUrl->GetHostName() << 
				" port=" << m_pUrl->GetPort());
			return RT_ERROR_FAILURE;
		}
	}

	RT_ASSERTE(!"CRtChannelHttpClientWithBrowerProxy::AsyncOpen, can't reach here!");
	return RT_ERROR_UNEXPECTED;
}

void CRtChannelHttpClientWithBrowerProxy::
OnConnectIndication(RtResult aReason, IRtTransport *aTrpt, 
					IRtAcceptorConnectorId *aRequestId)
{
	RtResult rv = aReason;
	RT_ASSERTE(m_pConnector.Get() == aRequestId);

	if (RT_FAILED(rv)) {
		RT_ASSERTE(!aTrpt);
		m_pConnector = NULL;
		
		// the first connection is TCP, skip it.
		if (m_dwConnectCount > 1) {
			if (rv != RT_ERROR_NETWORK_PROXY_SERVER_UNAVAILABLE) {
				rv = m_pHttpProxyManager->ClearCacheProxy(
					m_pUrl->GetHostName(), 
					m_pUrl->GetPort());
				if (rv != RT_ERROR_FOUND) {
					rv = RT_ERROR_NOT_FOUND;
					goto oncall;
				}
			}
			else {
				RT_ASSERTE(RT_FAILED(rv));
				goto oncall;
			}
		}

		rv = CRtChannelHttpClientWithBrowerProxy::AsyncOpen(m_pSinkTemp);
		if (RT_SUCCEEDED(rv)) 
			return;
	}

	if (RT_SUCCEEDED(rv)) {
		rv = aTrpt->OpenWithSink(this);
		if (RT_SUCCEEDED(rv)) {
			m_pTransport = aTrpt;
		}
	}

oncall:
	if (m_bOnConnectCalled) {
		if (RT_FAILED(rv)) {
			Disconnect(rv);
			RT_ASSERTE(m_pChannelSink);
			if (m_pChannelSink)
				m_pChannelSink->OnDisconnect(rv, this);
		}
		else {
			m_ParserResponse.Clear();
			m_bIsFirstSend = TRUE;
			CRtMessageBlock mbZero(
				m_strFirstSend.size(), 
				m_strFirstSend.c_str(), 
				CRtMessageBlock::DONT_DELETE, 
				m_strFirstSend.size());
			rv = SendData(mbZero);
		}
	}
	else {
		RT_ASSERTE(m_pChannelSink);
		if (m_pChannelSink)
			m_pChannelSink->OnConnect(rv, this);
		m_bOnConnectCalled = TRUE;
	}
}

void CRtChannelHttpClientWithBrowerProxy::OnObserve(LPCSTR aTopic, LPVOID aData)
{
	if (!strcmp(aTopic, "HttpProxyManager")) {
		RtResult rv = CRtChannelHttpClientWithBrowerProxy::AsyncOpen(m_pSinkTemp);
		if (RT_FAILED(rv)) {
			RT_ASSERTE(m_pChannelSink);
			if (m_pChannelSink)
				m_pChannelSink->OnConnect(rv, this);
		}
	} else {
		CRtChannelHttpClient::OnObserve(aTopic, aData);
	}
}

RtResult CRtChannelHttpClientWithBrowerProxy::Close_t(RtResult aReason)
{
	m_bOnConnectCalled = FALSE;
	m_pHttpProxyManager->RemoveObserver(this);
	return CRtChannelHttpClient::Close_t(aReason);
}

/*
void  CRtChannelHttpClient::OnResponseFinished()
{
#ifdef __STREAMING_SERVER__ 
	//RT_INFO_TRACE_THIS("CRtChannelHttpClient::OnResponseFinished()");
	m_bFullResponse = TRUE;
#endif
}
*/
CRtEventHttpClientAsyncOpen::CRtEventHttpClientAsyncOpen(CRtChannelHttpClient* inHttpClient)
: m_httpClient(inHttpClient)
{

}

CRtEventHttpClientAsyncOpen::~CRtEventHttpClientAsyncOpen()
{

}

RtResult CRtEventHttpClientAsyncOpen::OnEventFire()
{
	RT_INFO_TRACE_THIS("CRtEventHttpClientAsyncOpen::OnEventFire, httpClient="<<m_httpClient.Get());
	return m_httpClient->CRtChannelHttpClient::AsyncOpen(m_httpClient->m_pChannelSink);
}

